#region Usings

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

#endregion

namespace KanbanBoard.Client.View.DragAndDrop {
    public class DragAndDrop {
        public static readonly DependencyProperty OnDropProperty =
            DependencyProperty.RegisterAttached("OnDrop", typeof (DropCommand), typeof (DragAndDrop),
                                                new PropertyMetadata(null, OnDropChanged));

        public static readonly DependencyProperty OnPreviewDropProperty =
            DependencyProperty.RegisterAttached("OnPreviewDrop", typeof(DropPreviewCommand), typeof(DragAndDrop),
                                                new PropertyMetadata(null, OnPreviewDropChanged));

        private static object draggedItem;
        private static Popup popup;
        private static Popup mouseTracker;
        private static Point offset;

        public static DropCommand GetOnDrop(DependencyObject obj) {
            return (DropCommand) obj.GetValue(OnDropProperty);
        }

        public static void SetOnDrop(DependencyObject obj, DropCommand value) {
            obj.SetValue(OnDropProperty, value);
        }

        private static void OnDropChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
            var dragSource = obj as ItemsControl;
            if (dragSource != null) {
                dragSource.MouseLeftButtonDown += MouseLeftButtonDown;
            }
        }

        public static DropPreviewCommand GetOnPreviewDrop(DependencyObject obj) {
            return (DropPreviewCommand)obj.GetValue(OnPreviewDropProperty);
        }

        public static void SetOnPreviewDrop(DependencyObject obj, DropPreviewCommand value) {
            obj.SetValue(OnPreviewDropProperty, value);
        }

        private static void OnPreviewDropChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
            
        }

        private static void MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
            var sourceItemsControl = e.GetItemsControl();
            var draggedElement = e.GetItemControl();

            var sourceContainer = sourceItemsControl.GetDataContext();
            draggedItem = e.OriginalSource.GetDataContext();

            if (sourceContainer == null || draggedElement == null) return;

            offset = e.GetPosition(draggedElement);

            mouseTracker = new Popup();
            mouseTracker.CaptureMouse();
            mouseTracker.MouseMove += MouseMove;
            mouseTracker.MouseLeftButtonUp += MouseLeftButtonUp;

            popup = new Popup {
                                  Child = new ItemsControl {
                                                               ItemsSource = new List<object> {draggedItem},
                                                               ItemTemplate = sourceItemsControl.ItemTemplate,
                                                               Opacity = .5
                                                           }
                              };
        }

        private static void MouseMove(object sender, MouseEventArgs e) {
            var position = e.GetPosition(mouseTracker);
            popup.HorizontalOffset = (position.X - offset.X);
            popup.VerticalOffset = (position.Y - offset.Y);
            popup.IsOpen = true;

            var targetContainer = e.GetItemsControl();
            if (targetContainer == null) return;
            var dropPreview = GetOnPreviewDrop(targetContainer);
            if (dropPreview == null) return;
            var droppedOverItem = e.GetItemOfType(draggedItem.GetType());

            dropPreview.Execute(draggedItem, droppedOverItem);
        }

        private static void MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
            mouseTracker.ReleaseMouseCapture();
            popup.IsOpen = false;

            var targetContainer = e.GetItemsControl();
            var dropCommand = GetOnDrop(targetContainer);
            if (dropCommand == null) return;

            dropCommand.Execute(draggedItem);
        }
    }
}